#pragma once
#include <string>
#include "Dispatcher.hpp"
#include "EventLoop.hpp"
#include "Channel.hpp"
using namespace std;

class SelectDispatcher : public Dispatcher
{
public:
    // 初始化epoll,select,poll函数对应的数据块
    SelectDispatcher(EventLoop *evloop)
        : Dispatcher(evloop)
    {
        FD_ZERO(&m_readSet);
        FD_ZERO(&m_writeSet);
        m_name = "Select";
    }
    ~SelectDispatcher() {} // 实现多态需要将析构函数设置为虚函数
                           // 将下面的函数设置为虚函数后，子类就能对父类函数进行重写操作
                           // 添加
    int add() override
    {
        if (m_channel->getSocket() >= m_maxSize)
        {
            return -1;
        }
        setFdSet();

        return 0;
    }
    // 删除
    int remove() override
    {
        clearFdSet();
        m_channel->destoryCallback(const_cast<void *>(m_channel->getArg()));

        return 0;
    }
    // 修改
    int modify() override
    {
        if (m_channel->getEvent() & (int)FDEvent::ReadEvent)
        {
            FD_SET(m_channel->getSocket(), &m_readSet);
            FD_CLR(m_channel->getSocket(), &m_writeSet);
        }
        if (m_channel->getEvent() & (int)FDEvent::WriteEvent)
        {
            FD_SET(m_channel->getSocket(), &m_writeSet);
            FD_SET(m_channel->getSocket(), &m_readSet);
        }
        setFdSet();
        clearFdSet();
        return 0;
    }
    // 事件检测
    int dispatch(int timeout = 2) override
    {
        struct timeval val;
        val.tv_sec = timeout;
        val.tv_usec = 0; // 不置为0，就是一个随机数。结构体在算总时长时，会让sec和usec相加（单位会做统一处理再相加）如果不把usec置0，算出来的总时长与指定的时长不等，就不对
        fd_set rdtmp = m_readSet;
        fd_set wrtmp =m_writeSet;
        int count = select(m_maxSize, &rdtmp, &wrtmp, NULL, &val); // 数据传入内核，内核会对原始数据进行修改，因此需要对原始数据备份传入
        if (count == -1)
        {
            perror("select");
            exit(0);
        } // count！=-1，就有count个文件描述符被激活
        for (int i = 0; i <= m_maxSize; ++i)
        {
            // 依次取出有效数组的文件描述符并保存
            if (FD_ISSET(i, &rdtmp))
            {
                m_evLoop->eventActivate(i,(int)FDEvent::ReadEvent);
                
            }

            if (FD_ISSET(i, &wrtmp))
            {
                m_evLoop->eventActivate(i,(int)FDEvent::WriteEvent);
            }
        }
        return 0;
    }

private:
    void setFdSet()
    {
        if (m_channel->getEvent() & (int)FDEvent::ReadEvent)
        {
            FD_SET(m_channel->getSocket(), &m_readSet);
        }
        if (m_channel->getEvent() & (int)FDEvent::WriteEvent)
        {
            FD_SET(m_channel->getSocket(), &m_writeSet);
        }
    }
    void clearFdSet()
    {
        if (m_channel->getEvent() & (int)FDEvent::ReadEvent)
        {
            FD_CLR(m_channel->getSocket(), &m_readSet);
        }
        if (m_channel->getEvent() & (int)FDEvent::WriteEvent)
        {
            FD_CLR(m_channel->getSocket(), &m_writeSet);
        }
    }

private:
    fd_set m_readSet;
    fd_set m_writeSet;
    const int m_maxSize = 1024;
};